package org.jmeter.tcp.helmet.protobuf.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.protobuf.ByteString;
import com.google.protobuf.GeneratedMessage;
import com.google.protobuf.Message;
import java.lang.String;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import org.jmeter.tcp.helmet.protobuf.HelmetServer;
import org.jmeter.tcp.util.ByteUtils;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author liangsu
 * @version v1.0
 * @Description
 * @Date 2020/11/16 18:37
 * @since
 */
public class BuilderUtils {

    /**
     *
     * @param modelClass 如：Common.DeviceInfo.class
     * @param json
     * @return
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    public static GeneratedMessage.Builder build(Class<?> modelClass, JSONObject json, String space){
        GeneratedMessage.Builder builder = (GeneratedMessage.Builder) newBuilderByModelClass(modelClass);
        if(builder == null){
            return null;
        }

        Set<String> invokedMethods = new HashSet<>(); // 调用过的方法名称
        Method[] methods = builder.getClass().getDeclaredMethods();
        for (Method method : methods) {
            if(!method.getName().startsWith("set") && !method.getName().startsWith("add")){
                continue;
            }

            // 参数类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            if(parameterTypes.length != 1){
                continue;
            }

            Class<?> parameterType = parameterTypes[0];
//            if(parameterType.isAssignableFrom(ByteString.class)){
//                continue;
//            }
            if(parameterType.getName().endsWith("$Builder")){
                continue;
            }

            // 判断是否调用过这个方法，主要用于避免情况：
            // setId(java.lang.String  id) 与 setId(com.google.protobuf.ByteString  id)
            if(invokedMethods.contains(method.getName())){
                continue;
            }
            invokedMethods.add(method.getName());

            // 属性名称
            String filedName = getFiledNameBySetter(method);

//            System.out.println(space + modelClass.getName() + "."
//                    + method.getName() + "(" + parameterType.getName() + "  " + filedName + "" + ")");

            if(isPrimitive(parameterType) || parameterType.isAssignableFrom(ByteString.class)){ //
                // string
                if(parameterType.isAssignableFrom(String.class)){
                    String value = json.getString(filedName);
                    setValue(builder, method, value);
                }
                // integer
                else if(parameterType.isAssignableFrom(Integer.class)){
                    Integer value = json.getInteger(filedName);
                    setValue(builder, method, value);
                }else if(parameterType.isAssignableFrom(int.class)){
                    Integer value = json.getInteger(filedName);
                    setValue(builder, method, value);
                }
                // float
                else if(parameterType.isAssignableFrom(Float.class)){
                    Float value = json.getFloat(filedName);
                    setValue(builder, method, value);
                }else if(parameterType.isAssignableFrom(float.class)){
                    float value = json.getFloatValue(filedName);
                    setValue(builder, method, value);
                }
                // long
                else if(parameterType.isAssignableFrom(Long.class)){
                    Long value = json.getLong(filedName);
                    setValue(builder, method, value);
                }else if(parameterType.isAssignableFrom(long.class)){
                    long value = json.getLongValue(filedName);
                    setValue(builder, method, value);
                }
                // char
                else if(parameterType.isAssignableFrom(Character.class)){
                    String value = json.getString(filedName);
                    setValue(builder, method, value.toCharArray()[0]);
                }else if(parameterType.isAssignableFrom(char.class)){
                    String value = json.getString(filedName);
                    setValue(builder, method, value.toCharArray()[0]);
                }
                // boolean
                else if(parameterType.isAssignableFrom(Boolean.class)){
                    Boolean value = json.getBoolean(filedName);
                    setValue(builder, method, value);
                }else if(parameterType.isAssignableFrom(boolean.class)){
                    boolean value = json.getBooleanValue(filedName);
                    setValue(builder, method, value);
                }
                // byte
                else if(parameterType.isAssignableFrom(Byte.class)){
                    byte[] value = json.getBytes(filedName);
                    setValue(builder, method, value);
                }else if(parameterType.isAssignableFrom(byte.class)){
                    byte[] value = json.getBytes(filedName);
                    setValue(builder, method, value);
                }
                // ByteString
                else if(parameterType.isAssignableFrom(ByteString.class)){
                    byte[] value = json.getBytes(filedName);
                    ByteString byteString = ByteString.copyFrom(value);
                    setValue(builder, method, byteString);
                }

            }else{ // 其它类

//                System.out.println("-------------------------------------");
                JSONObject subObject = json.getJSONObject(filedName);
                if(subObject != null){
                    GeneratedMessage.Builder subBuilder = build(parameterType, subObject,
                            space + "--");
                    Message subMsg = subBuilder.build();
                    setValue(builder, method, subMsg);
                }

            }

        }

        return builder;
    }

    public static boolean isPrimitive(Class<?> parameterType){
        if(parameterType.isPrimitive()
                || parameterType.isAssignableFrom(String.class)
                || parameterType.isAssignableFrom(Integer.class)
                || parameterType.isAssignableFrom(Float.class)
                || parameterType.isAssignableFrom(Double.class)
                || parameterType.isAssignableFrom(Character.class)
                || parameterType.isAssignableFrom(Byte.class)){
            return true;
        }
        return false;
    }

    public static String getFiledNameBySetter(Method setMethod){
        String name = setMethod.getName();
        char[] chars = name.toCharArray();
        if(chars.length >= 3){
            chars[3] = (char)(chars[3] | 32);
        }
        return new String(chars, 3, chars.length - 3);
    }

    /**
     * 通过model类，生成builder
     * 如：通过类Common.DeviceInfo，调用newBuilder方法，生成 Common.DeviceInfo.Builder
     * @param modelClass
     * @return
     */
    public static Object newBuilderByModelClass(Class<?> modelClass){
        Object builder = null;
        try {
            Method method = modelClass.getDeclaredMethod("newBuilder");
            if(method != null){
                builder = method.invoke(null, null);
            }
        } catch (NoSuchMethodException e) {
            System.err.println(modelClass.getName() + " no method newBuilder");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return builder;
    }

    public static void setValue(Object instance, Method method, Object value){
        if(value != null){
            try {
                method.setAccessible(true);
                method.invoke(instance, value);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 将消息转换为ByteBuffer
     * @param h2SMessage
     * @return
     */
    public static ByteBuffer messageToByteBuffer(HelmetServer.H2SMessage h2SMessage){
        byte[] bytes = h2SMessage.toByteArray();
        ByteBuffer buffer = ByteBuffer.allocate(bytes.length + 1 + 4);
        buffer.put((byte)0x50);
        buffer.put(ByteUtils.reverse(ByteUtils.int2Bytes(bytes.length, 4)));
        buffer.put(bytes);
        buffer.flip();
        return buffer;
    }

    public static ByteBuf messageToByteBuf(HelmetServer.H2SMessage h2SMessage){
        byte[] bytes = h2SMessage.toByteArray();

        ByteBuf byteBuf = PooledByteBufAllocator.DEFAULT.buffer(bytes.length + 1 + 4);
        byteBuf.writeByte((byte)0x50);
        byteBuf.writeBytes(ByteUtils.reverse(ByteUtils.int2Bytes(bytes.length, 4)));
        byteBuf.writeBytes(bytes);

        return byteBuf;
    }

}
